#include "Keyboard.h"

Keyboard::Keyboard()
{
	DirectInput = NULL;
	KeyboardDevice = NULL;

	Delay = 0;

	AddKeyInfo(KEY_0, '0');
	AddKeyInfo(KEY_1, '1');
	AddKeyInfo(KEY_2, '2');
	AddKeyInfo(KEY_3, '3');
	AddKeyInfo(KEY_4, '4');
	AddKeyInfo(KEY_5, '5');
	AddKeyInfo(KEY_6, '6');
	AddKeyInfo(KEY_7, '7');
	AddKeyInfo(KEY_8, '8');
	AddKeyInfo(KEY_9, '9');

	AddKeyInfo(KEY_NUMPAD0, '0');
	AddKeyInfo(KEY_NUMPAD1, '1');
	AddKeyInfo(KEY_NUMPAD2, '2');
	AddKeyInfo(KEY_NUMPAD3, '3');
	AddKeyInfo(KEY_NUMPAD4, '4');
	AddKeyInfo(KEY_NUMPAD5, '5');
	AddKeyInfo(KEY_NUMPAD6, '6');
	AddKeyInfo(KEY_NUMPAD7, '7');
	AddKeyInfo(KEY_NUMPAD8, '8');
	AddKeyInfo(KEY_NUMPAD9, '9');

	AddKeyInfo(KEY_A, 'a', TRUE);
	AddKeyInfo(KEY_B, 'b', TRUE);
	AddKeyInfo(KEY_C, 'c', TRUE);
	AddKeyInfo(KEY_D, 'd', TRUE);
	AddKeyInfo(KEY_E, 'e', TRUE);
	AddKeyInfo(KEY_F, 'f', TRUE);
	AddKeyInfo(KEY_G, 'g', TRUE);
	AddKeyInfo(KEY_H, 'h', TRUE);
	AddKeyInfo(KEY_I, 'i', TRUE);
	AddKeyInfo(KEY_J, 'j', TRUE);
	AddKeyInfo(KEY_K, 'k', TRUE);
	AddKeyInfo(KEY_L, 'l', TRUE);
	AddKeyInfo(KEY_M, 'm', TRUE);
	AddKeyInfo(KEY_N, 'n', TRUE);
	AddKeyInfo(KEY_O, 'o', TRUE);
	AddKeyInfo(KEY_P, 'p', TRUE);
	AddKeyInfo(KEY_Q, 'q', TRUE);
	AddKeyInfo(KEY_R, 'r', TRUE);
	AddKeyInfo(KEY_S, 's', TRUE);
	AddKeyInfo(KEY_T, 't', TRUE);
	AddKeyInfo(KEY_U, 'u', TRUE);
	AddKeyInfo(KEY_V, 'v', TRUE);
	AddKeyInfo(KEY_W, 'w', TRUE);
	AddKeyInfo(KEY_X, 'x', TRUE);
	AddKeyInfo(KEY_Y, 'y', TRUE);
	AddKeyInfo(KEY_Z, 'z', TRUE);

	AddKeyInfo(KEY_SPACE,     ' ');
}

Keyboard::~Keyboard()
{
	// Empty
}

bool Keyboard::Setup(HWND Window, bool Exclusive)
{
	DWORD Flags;

	Shutdown();

	if(FAILED(DirectInput8Create(GetModuleHandle(NULL), DIRECTINPUT_VERSION, IID_IDirectInput8, reinterpret_cast<VOID**>(&DirectInput), NULL)))
	{
		Error::Instance().SetMessage("Unable to create a DirectInput object!");
		return false;
	}

	if(FAILED(DirectInput->CreateDevice(GUID_SysKeyboard, &KeyboardDevice, NULL)))
	{
		Error::Instance().SetMessage("Unable to create a DirectInput keyboard device!");
		return false;
	}

	if(FAILED(KeyboardDevice->SetDataFormat(&c_dfDIKeyboard)))
	{
		Error::Instance().SetMessage("Unable to set the DirectInput keyboard data format!");
		return false;
	}

	Flags = DISCL_FOREGROUND | (Exclusive ? DISCL_EXCLUSIVE : DISCL_NONEXCLUSIVE);

	if(FAILED(KeyboardDevice->SetCooperativeLevel(Window, Flags)))
	{
		Error::Instance().SetMessage("Unable to set the DirectInput keyboard cooperative level!");
		return false;
	}

	KeyboardDevice->Acquire();

	return true;
}

void Keyboard::Shutdown()
{
	if(KeyboardDevice != NULL) 
	{
		KeyboardDevice->Unacquire();
		KeyboardDevice->Release();
		KeyboardDevice = NULL;
	}

	if(DirectInput != NULL)
	{
		DirectInput->Release();
		DirectInput = NULL;
	}

	Delay = 0;
}

void Keyboard::Update()
{
	INT i;
	BYTE KeyboardState[NR_OF_KEYBOARD_KEYS];

	if(KeyboardDevice == NULL) return;

	ZeroMemory(&KeyboardState, sizeof(KeyboardState));
	if(FAILED(KeyboardDevice->GetDeviceState(sizeof(KeyboardState), &KeyboardState)))
	{
		KeyboardDevice->Acquire();

		ZeroMemory(&KeyboardState, sizeof(KeyboardState));
		if(FAILED(KeyboardDevice->GetDeviceState(sizeof(KeyboardState), &KeyboardState)))
		{
			return;
		}
	}

	AnyKeyState = KeyboardKeyState();

	for(i = 0; i < NR_OF_KEYBOARD_KEYS; i++)
	{
		KeyState[i].mKeyPressed = FALSE;
		KeyState[i].mKeyReleased = FALSE;

		if(KeyState[i].mDelay > 0) KeyState[i].mDelay--;

		if(KeyboardState[i] & 0x80)
		{
			if(!KeyState[i].mKeyDown)
			{
				KeyState[i].mDelay = Delay;
				KeyState[i].mKeyPressed = TRUE;
				AnyKeyState.mKeyPressed = TRUE;
			}

			KeyState[i].mKeyDown = TRUE;

			if(ValidKeyDelay(KeyState[i].mDelay))
			{
				AnyKeyState.mKeyDown = TRUE;
			}
		}
		else
		{
			if(KeyState[i].mKeyDown)
			{
				KeyState[i].mDelay = 0;
				KeyState[i].mKeyReleased = TRUE;
				AnyKeyState.mKeyReleased = TRUE;
			}

			KeyState[i].mKeyDown = FALSE;
		}
	}
}

bool Keyboard::KeyDown(s32 Key) const
{
	if(!ValidKey(Key)) return FALSE;

	return KeyState[Key].mKeyDown && ValidKeyDelay(KeyState[Key].mDelay);
}

bool Keyboard::KeyPressed(s32 Key) const
{
	if(!ValidKey(Key)) return FALSE;

	return KeyState[Key].mKeyPressed;
}

bool Keyboard::KeyReleased(s32 Key) const
{
	if(!ValidKey(Key)) return FALSE;

	return KeyState[Key].mKeyReleased;
}

bool Keyboard::AnyKeyDown() const
{
	return AnyKeyState.mKeyDown;
}

bool Keyboard::AnyKeyPressed() const
{
	return AnyKeyState.mKeyPressed;
}

bool Keyboard::AnyKeyReleased() const
{
	return AnyKeyState.mKeyReleased;
}


void Keyboard::SetDelay(s32 NewDelay)
{
	Delay = (NewDelay > 0) ? NewDelay : 0;
}

s32 Keyboard::GetDelay() const
{
	return Delay;
}

bool Keyboard::ValidKey(s32 Key) const
{
	return (Key >= 0) && (Key < NR_OF_KEYBOARD_KEYS);
}

bool Keyboard::ValidKeyDelay(s32 KeyDelay) const
{
	return (KeyDelay <= 0) || (KeyDelay >= Delay);
}

void Keyboard::AddKeyInfo(s32 Key, c8 Character, bool Letter)
{
	KeyInfo[Key].mValid = TRUE;
	KeyInfo[Key].mLetter = Letter;
	KeyInfo[Key].mCharacter = Character;
}